package com.example.oauth.config;



import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

@AutoConfigureAfter(value = BeanConfiguration.class)
@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfigurer extends AuthorizationServerConfigurerAdapter {
	
	@Autowired
	private UserDetailsService userDetailsService;
	
	@Autowired
	private ClientDetailsService clientDetailsService;
	
	@Autowired
	private AuthenticationEntryPoint jsonRespAuthenticationEntryPoint;
	
	@Autowired
	private AuthenticationFailureHandler authenticationFailureHandler;
	
	
	/**
	 * 	用来配置客户端详情服务
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		//==========使用内存储客户端信息=============
//		clients.inMemory() // 使用in-memory存储
//				.withClient("client") // client_id
//				.secret(bcryptPasswordEncoder.encode("secret")) // client_secret
//				.authorizedGrantTypes("authorization_code") // 该client允许的授权类型
//				// .autoApprove(true) //登录后绕过批准询问(/oauth/confirm_access)
//				.redirectUris("http://www.baidu.com")
//				.scopes("app"); // 允许的授权范围
		
		//==========使用数据库存储客户端信息=============
		clients.withClientDetails(clientDetailsService);
		
	}

	
	/**
	 * 	用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)，还有token的存储方式(tokenStore)；
	 */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //配置认证管理器
        endpoints
                //配置用户服务
                .userDetailsService(userDetailsService)
                //注意：此处tokenService不能使用注入，否则不生效
                .tokenServices(tokenService());
        
    }
    
    
	/**
	 * 	用来配置令牌端点(Token Endpoint)的安全约束；
	 * 
	 * =======可选配置=========
	 *	 资源服务器与认证服务器分离时
	 * 	例如资源服务器 ResourceServerTokenServices 的  RemoteTokenServices 实现类时
	 * 	需要授权资源服务器权限：http://localhost:8888/oauth/check_token
	 */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		security
			.tokenKeyAccess("permitAll()")
			.checkTokenAccess("isAuthenticated()")
			.allowFormAuthenticationForClients()
			.authenticationEntryPoint(jsonRespAuthenticationEntryPoint);
		
	}
	
	
	
	
    
    /**
     * 	认证服务tokenService
     * 
     * DefaultTokenServices 实现了 AuthorizationServerTokenServices, ResourceServerTokenServices接口
     * 			AuthorizationServerTokenServices：授权服务器使用
     * 			ResourceServerTokenServices：资源服务器使用
     * @return
     * 
     * 
     * 
     */
    @Bean
    @Primary
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        //当前配置为将jwtToken存储到redis中
        tokenServices.setTokenStore(tokenStore);
        tokenServices.setTokenEnhancer(jwtAccessTokenConverter);
        
        //开启支持refresh_token，此处如果之前没有配置，启动服务后再配置重启服务，可能会导致不返回token的问题，解决方式：清除redis对应token存储
        tokenServices.setSupportRefreshToken(true);
        //复用refresh_token
        tokenServices.setReuseRefreshToken(true);
        //token有效期，设置1小时
        tokenServices.setAccessTokenValiditySeconds(1 * 60 * 60);
        //refresh_token有效期，设置一周
        tokenServices.setRefreshTokenValiditySeconds(7 * 24 * 60 * 60);
        
        tokenServices.setClientDetailsService(clientDetailsService);
        
        return tokenServices;
    }
    
    
    @Autowired 
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    
    //切换token存储 redisTokenStore , jwtTokenStore
    @Resource(name="redisTokenStore") 
    private TokenStore tokenStore;

}

